from collections import defaultdict
from typing import Union, Tuple, List, Any, Dict

import ujson
from peewee import IntegrityError, DoesNotExist

from cache.attribute_cache import AttributeCache
from common.g.g import G_LOGGER
from common.model.attribute.attribute import TotalAttributeKey, TotalAttributeValue
from common.utils.depends.common import Page
from common.utils.resp import ERROR, RC
from common.v.v import DATAORCODE
from schema.req_schema.attribute_req_schema import AddNewAttributeModel, AddNewAttributeValueModel
from service.base import Service


class AttributeService(Service):
    CACHE_CLS = AttributeCache

    async def _add_attribute_key(self, attribute):
        attribute_id = await TotalAttributeKey.add_new_instance(self.mgw, attribute_name=attribute.attribute_name,
                                                                attribute_key_code=attribute.attribute_code)
        return attribute_id

    async def _add_attribute_value(self, attribute_id, attribute_value_list):
        value_list = []
        for value_dict in attribute_value_list:
            value_list.append(dict(attribute_key_id=attribute_id,
                                   attribute_value_code=value_dict.attribute_code,
                                   attribute_value=value_dict.attribute_value))
        query = TotalAttributeValue.insert_many(value_list)
        await self.mgw.execute(query)

    async def add_new_attribute(self, attribute: AddNewAttributeModel) -> Tuple[bool, Union[int, DATAORCODE]]:
        """
        创建一个新的属性
        Args:
            attribute:
        Returns:
            attribute_id: int
        """
        async with self.mgw.atomic() as f:
            try:
                attribute_id = await self._add_attribute_key(attribute)
            except IntegrityError:
                await f.rollback()
                return False, DATAORCODE(data=None, code=RC.ATTRIBUTE_ALREADY_EXIST, msg="属性已经存在")
            try:
                await self._add_attribute_value(attribute_id, attribute.attribute_value_list)
            except IntegrityError:
                await f.rollback()
                return False, DATAORCODE(data=None, code=RC.ATTRIBUTE_ALREADY_EXIST, msg="属性值已经存在")

            await self.cache.delete_old_cache()

        return True, DATAORCODE(data={"attribute_id": attribute_id})

    async def get_attribute_tree(self, page_info: Page) -> Tuple[bool, DATAORCODE]:
        """
        获取属性树
        Args:
            page_info:
        Returns:

        """
        cache_key = f"attribute:{page_info.page}_{page_info.page_size}"
        try:
            cache_data = await self.cache.get_attribute_tree(cache_key)
            if cache_data:
                data = ujson.loads(cache_data)
                count = len(data)
                return True, DATAORCODE(data={"data":data, "total": count})
        except Exception as e:
            G_LOGGER.exception(f"查询属性缓存信息失败:{e}")
        try:
            query = TotalAttributeKey.select(TotalAttributeKey.attribute_id, TotalAttributeKey.attribute_name)
            count = await self.mgw.count(query)
            key_list = await self.mgw.execute(query.paginate(page_info.page, page_info.page_size).dicts())
            if key_list:
                attribute_key_id_list = [d.get("attribute_id") for d in key_list]
                value_query = TotalAttributeValue.select(TotalAttributeValue.attribute_value_id,
                                                         TotalAttributeValue.attribute_value,
                                                         TotalAttributeValue.attribute_key_id). \
                    where(TotalAttributeValue.attribute_key_id.in_(attribute_key_id_list),
                          TotalAttributeValue.removed == False).dicts()
                value_list = await self.mgw.execute(value_query)

                key_to_value_dict = defaultdict(list)
                for value in value_list:
                    key_to_value_dict[value.pop("attribute_key_id")].append(value)
                for key in key_list:
                    key["values"] = key_to_value_dict.get(key.get("attribute_id"), [])
        except Exception as e:
            G_LOGGER.exception(f"查询属性DB信息失败:{e}")
            return False, DATAORCODE(data=None, code=RC.DB_ERROR, msg="服务异常")
        try:
            await self.cache.set_attribute_tree(cache_key, ujson.dumps(list(key_list)), ex=30 * 60)
        except Exception as e:
            G_LOGGER.exception(f"更新属性缓存信息失败:{e},不影响业务")
        return True, DATAORCODE(data={"data": list(key_list), "count": int(count)})

    async def add_attribute_value(self, attribute_id, value_list: List[AddNewAttributeValueModel]):
        try:
            await TotalAttributeKey.get_instance(self.mgw,
                                                 condition={"attribute_id": attribute_id, "removed": False},
                                                 fields=["attribute_id"])
        except DoesNotExist:
            return False, DATAORCODE(data=None, code=RC.ATTRIBUTE_NOT_EXIST, msg="属性不存在")

        async with self.mgw.atomic() as f:
            try:
                await self._add_attribute_value(attribute_id, value_list)
            except IntegrityError:
                await f.rollback()
                return False, DATAORCODE(data=None, code=RC.ATTRIBUTE_ALREADY_EXIST, msg="属性值已经存在")
            except Exception as e:
                await f.rollback()
                G_LOGGER.exception(e)
                return False, DATAORCODE(data=None, code=RC.DB_ERROR, msg="服务异常")
            await self.cache.delete_old_cache()
        return True, DATAORCODE()

    async def delete_attribute(self, attribute_id):
        condition = {
            "attribute_id": attribute_id,
            "removed": False
        }
        try:
            delete_row = await TotalAttributeKey.update_instance(self.mgw, condition=condition, update_fields={"removed": True})
        except Exception as e:
            G_LOGGER.exception(e)
            return False, DATAORCODE(data=None, code=RC.DB_ERROR, msg="服务异常")
        return True, DATAORCODE(data={"delete_row": delete_row})